#include "./split.cpp"

namespace utils {
    class AlgoType : public std::map<std::string, std::string> {
        public:
        using std::map<std::string, std::string>::map;
        AlgoType& operator+=(const AlgoType& src) {
            for (const auto& [key, value] : src)
                if (this->find(key) == this->end()) this->insert({key, value});
            return *this;
        }
        std::string serialize() const {
            std::string buf;
            buf.push_back('{');
            for (const auto [key, val] : *this) {
                buf.insert(buf.end(), key.begin(), key.end());
                buf.push_back(':');
                buf.insert(buf.end(), val.begin(), val.end());
                buf.push_back(',');
            }
            buf.push_back('}');
            return buf;
        }
        void deserialize(std::string buf) {
            this->clear();
            assert(buf.front() == '{' && buf.back() == '}');
            buf.erase(buf.begin()), buf.erase(prev(buf.end()));
            std::vector<std::string> items = split(buf, ',');
            for (std::string x : items) {
                std::vector<std::string> y = split(x, ':');
                assert(y.size() == 2), this->insert({y[0], y[1]});
            }
        }
        std::string genCompPara() const { // Generate Compilation Parameters
            std::string ret;
            ret += " -O2 -std=c++17 -DLAUNCHERMODE ";
            for (const auto [key, val] : *this) {
                ret += "-D" + key + "Code";
                ret += "=\"./algo/" + key + "/" + val + ".cpp\" ";
            }
            return ret;
        }
    };
    class TestRes : public std::map<std::string, std::pair<ll, ll>> {
        public:
        using std::map<std::string, std::pair<ll, ll>>::map;
        TestRes& operator+=(const TestRes& src) {
            for (const auto& [key, value] : src) {
                auto it = this->find(key);
                if (it == this->end()) this->insert({key, value});
                else {
                    it->second.first += value.first;
                    it->second.second += value.second;
                }
            }
            return *this;
        }
        std::string serialize() const {
            std::string buf;
            buf.push_back('{');
            for (const auto [key, val] : *this) {
                buf.insert(buf.end(), key.begin(), key.end());
                buf.push_back(':');
                std::string s1 = std::to_string(val.first);
                std::string s2 = std::to_string(val.second);
                std::string s = s1 + '-' + s2;
                buf.insert(buf.end(), s.begin(), s.end());
                buf.push_back(',');
            }
            buf.push_back('}');
            return buf;
        }
        void deserialize(std::string buf) {
            this->clear();
            assert(buf.front() == '{' && buf.back() == '}');
            buf.erase(buf.begin()), buf.erase(prev(buf.end()));
            std::vector<std::string> items = split(buf, ',');
            for (std::string x : items) {
                std::vector<std::string> y = split(x, ':');
                assert(y.size() == 2);
                std::vector<std::string> z = split(y[1], '-');
                this->insert({y[0], std::make_pair(stoll(z[0]), stoll(z[1]))});
            }
        }
    };
    class StatData : public std::map<AlgoType, TestRes> {
        public:
        using std::map<AlgoType, TestRes>::map;
        std::string serialize() {
            std::string buf;
            buf.push_back('[');
            for (const auto [key, val] : *this) {
                std::string keyStr = key.serialize();
                std::string valStr = val.serialize();
                buf.insert(buf.end(), keyStr.begin(), keyStr.end());
                buf.push_back('|');
                buf.insert(buf.end(), valStr.begin(), valStr.end());
                buf.push_back(';');
            }
            buf.push_back(']');
            return buf;
        }
        void deserialize(std::string buf) {
            this->clear();
            assert(buf.front() == '[' && buf.back() == ']');
            buf.erase(buf.begin()), buf.erase(prev(buf.end()));
            std::vector<std::string> items = split(buf, ';');
            for (std::string x : items) {
                std::vector<std::string> y = split(x, '|');
                assert(y.size() == 2);
                AlgoType t1; t1.deserialize(y[0]);
                TestRes t2; t2.deserialize(y[1]);
                this->insert({t1, t2});
            }
        }
    };
}

using utils::AlgoType;
using utils::TestRes;
using utils::StatData;
